/* 
 * File:   script.c
 * Author: bjmey
 *
 * Created on September 5, 2007, 9:51 PM
 */

#include <stdio.h>
#include <stdlib.h>
#include "lua.h"
#include "lauxlib.h"
#include "memory.h"
#include "rope.h"
#include "script.h"


static void *l_alloc (void *ud, void *ptr, size_t osize,
                                                size_t nsize) {
       (void)ud;  (void)osize;  /* not used */
       if (nsize == 0) {
         eruta_free(ptr);
         return NULL;
       }
       else
         return eruta_realloc(ptr, nsize);
}

#define SCRIPT_WRAP(self, function, ARGS)  do { if(!self) return NULL;\
  										   function(self, ARGS ) ; return self; } while (0) ; return NULL
#define SCRIPT_WRAP_NA(self, function) do {  if(!self) return NULL;\
  										 function(self) ; return self; } while (0) ; return NULL										 

Script script_make() {
    Script result = lua_newstate(l_alloc, "ERUTA Script allocated memory.");
	luaL_openlibs(result);
	/* Allow use of default Lua libraries. Perhaps this needs to be more limited. */
	return result;
}

Script script_free(Script self) {
    lua_close(self);
    return NULL;
}

Script script_eval_c(Script self, const char * script ) {
  int res =0;
	luaL_loadstring(self, script); 
	res = lua_pcall(self, 0, LUA_MULTRET, 0);
	if(res) {
		printf("Lua error: %d %s\n", res, lua_tostring(self, 1));
    	lua_remove(self, 1);
    	return NULL;
	}
	return self;
}

Script script_eval_r(Script self, Rope script ) {
    return script_eval_c(self, rope_c(script));
}

Script script_run(Script self, const char *filename) {
	int res = luaL_dofile (self, filename);
	if(res) {
		printf("Lua error: %d %s\n", res, lua_tostring(self, 1));
    	lua_remove(self, 1);
    	return NULL;
	}
	return self;
}

Script script_push_string(Script self , const char * str ) {
 	SCRIPT_WRAP(self, lua_pushstring, ( str ) );
}

Script script_get_table(Script self, int index) {
	SCRIPT_WRAP(self, lua_gettable, ( index ));
}

Script script_set_table (Script self, int index) { 
	SCRIPT_WRAP(self, lua_settable,  index); 
}


Script script_setglob_string(Script self, const char *varname, const char *value ) {
	script_push_string(self, varname);
	script_push_string(self, value);
     return script_set_table(self, LUA_GLOBALSINDEX);
} 


Script script_get_global(Script self, const char *varname) {
     return script_get_table(script_push_string(self, varname), LUA_GLOBALSINDEX);
} 

Script script_get_globalx(Script self, const char *varnamex, const char * assignto) {
    Rope aid = rope_format("%s=%s", assignto, varnamex);
    Script result = script_get_global(script_eval_c(self, rope_c(aid)), assignto);
	rope_free(aid); /* Free local variable aid */
	return result;
}

/*
typedef int (*Luacheck) (Script self, int index);

int script_check(Script self,  Luacheck check) {
	return check(self, 1);
} 
*/


Rope script_get_rope(Script self, const char * globalname, const char *def) {
  script_get_globalx(self, globalname, "xggxs");
  if(lua_isstring(self, 1)) {
    Rope result = rope_make(lua_tostring(self, 1));
    lua_remove(self, 1);
    return result;
  }
  if(def) { return rope_make(def); }
  else { return NULL; }
}

double script_get_number(Script self, const char * globalname, double def) {
  double result;
  script_get_globalx(self, globalname, "xggxn" );
  if(lua_isnumber(self, 1)) {
    result = (lua_tonumber(self, 1));
    lua_remove(self, 1);
    return result;
  }
  if(def) { return def; } else { return 0.0; }
}




#ifdef _NO_WAY

Script script_setdouble(Script self, const char *varname, double val)
{  
    if(!self) return self;    
    lua_pushstring(self,varname);
    lua_pushnumber(self, val);       
    lua_rawset(self, LUA_GLOBALSINDEX);
    return self;
}
  
Script script_setint(Script self, const char *varname, long val)
{  
    lua_pushstring(self, varname);
    lua_pushnumber(self, val);       
    lua_rawset(self, LUA_GLOBALSINDEX);
    return self;
}
  
Script script_setbool(Script self, const char *varname, int val)
{  
    lua_pushstring(self, varname);
    lua_pushboolean(self, val);       
    lua_rawset(self, LUA_GLOBALSINDEX);
    return self;
}
  
Script setstring(Script self, const char *varname,  const Rope val)
{  
    pushstring(self, varname);
    pushstring(self, rope_c(val));       
    rawset(self, LUA_GLOBALSINDEX);
}
  
Script setfunction(const char *varname, lua_CFunction f, int index = LUA_GLOBALSINDEX)  
{ 
     pushstring(varname);
     pushcfunction(f);  
     rawset(index);
}

Script settablefunction(const char *tablename, const char *varname, lua_CFunction f)  
{ 
     tableintable(tablename, LUA_GLOBALSINDEX);
     pushstring(varname);
     pushcfunction(f);  
     rawset(-3);
     pop();     
}
  
Script setregistrytablefunction(const char *tablename, const char *varname, lua_CFunction f)  
{ 
     tableintable(tablename, LUA_REGISTRYINDEX);
     pushstring(varname);
     pushcfunction(f);  
     rawset(-3);
     pop();     
}
 

double getdouble(const char *varname, double def)
{  
    getglobalx(varname);
    if(!isnumber()) { pop(1); return def; }     
    return tonumber(); 
}
  
  long getint(const char *varname, long def)
  {  
    getglobalx(varname);
    if(!isnumber()) { pop(1); return def; }     
    return  (int) tonumber(); 
  }
  
  bool getbool(const char *varname, bool def)
  {  
    getglobalx(varname);
    if(!isboolean()) { pop(1); return def; }     
    return  (bool)toboolean(); 
  }
  
  Rope getstring(const char *varname,  const Rope def)
  { 
    tsukistring res;      
    getglobalx(varname);
    if(!isstring()) { pop(1); return def; }     
    res = tostring();
    res += ""; /* Force copy-on-write */
    pop(1); 
    return res; 
  }
  
  Rope get(const char *varname,  const Rope def)
  { return getstring(varname,def); }
  double get(const char *varname, double def)
  { return getdouble(varname,def); }
  
  Script argdiscard(int count = 0)
  {  if(gettop() > count) remove(1);    }
  
  double argdouble(double def)
  { 
    double res;        
    if(!isnumber(1)) { argdiscard(); return def; }     
    res = tonumber(1); 
    argdiscard();
    return res;
  }
  
  long argint(long def)
  {  
    long res;      
    if(!isnumber(1)) { argdiscard(); return def; }     
    res = (long) tonumber(1); 
    argdiscard();
    return res;
  }
  
  bool argbool(bool def)
  {  
    bool res;      
    if(!isboolean(1)) { argdiscard(); return def; }     
    res = (bool)toboolean(1); 
    return res;
  }
  
  Rope argstring(const Rope def)
  { 
    tsukistring res;          
    if(!isstring(1)) { argdiscard(1); return def; }     
    res = tostring(1);
    res += ""; /* Force copy-on-write */
    argdiscard();
    return res; 
  }

/*
  int checkstack (int extra) { return lua_checkstack (mystate, extra); }
  int gettop     (Script)      { return lua_gettop     (mystate);        }
  Script settop    (int index)     { lua_settop    (mystate, index); }
  Script pushvalue (int index)     { lua_pushvalue (mystate, index); }
  Script remove    (int index)     { lua_remove    (mystate, index); }
  Script insert    (int index)     { lua_insert    (mystate, index); }
  Script replace   (int index)     { lua_replace   (mystate, index); }
  Script pop       (int index = 1) { lua_pop       (mystate, index); }
*/
  /* Low level stack interface wrappers. */
/*
  int gettype     (int index) { return lua_type       (mystate, index); }
*/
  /* Exception to the naming scheme!!! */
/*
  int isnil       (int index = STACKTOP) { return lua_isnil      (mystate, index); }
  int isboolean   (int index = STACKTOP) { return lua_isboolean  (mystate, index); }
  int isnumber    (int index = STACKTOP) { return lua_isnumber   (mystate, index); }
  int isstring    (int index = STACKTOP) { return lua_isstring   (mystate, index); }
  int istable     (int index = STACKTOP) { return lua_istable    (mystate, index); }
  int isfunction  (int index = STACKTOP) { return lua_isfunction (mystate, index); }
  int iscfunction (int index = STACKTOP) { return lua_iscfunction(mystate, index); }
  int isuserdata  (int index = STACKTOP) { return lua_isuserdata (mystate, index); }
*/
/*
  Script seterror(const tsukistring & err) {mylasterror = err; }
  const tsukistring & getlasterror(Script) {return mylasterror;}

  const char *gettypename(int type) { return lua_typename (mystate,  type); }
*/
  /* Exception to the naming scheme!!! */
  
  /* This concludes the data type checking and naming low level functions. */
/*  
int          toboolean     (int index = STACKTOP) 
  { return lua_toboolean(mystate, index);     }
  lua_Number   tonumber      (int index = STACKTOP) 
  { return lua_tonumber (mystate, index);     }
  const char * tostring (int index = STACKTOP) 
  {  
     return lua_tostring(mystate, index);
  }
  size_t       strlen        (int index = STACKTOP) 
  { return lua_strlen   (mystate, index);     } 
  Script *       touserdata    (int index = STACKTOP) 
  { return lua_touserdata(mystate, index);    }
  lua_CFunction tocfunction  (int index = STACKTOP)
  { return lua_tocfunction (mystate, index);  }
*/
   /* Low level access to data on stack. */
/*
  int equal         (int index1, int index2)
  { return lua_equal (mystate, index1,  index2); }
  int lessthan      (int index1, int index2)
  { return lua_lessthan (mystate, index1,  index2); }
*/
  /* Low level comparison. */
/*
  Script pushboolean  (int b)
  { lua_pushboolean ( mystate, b );    }
  Script pushlstring  (const char * s, size_t len)
  { lua_pushlstring (mystate, s, len); }
  Script pushnumber   (lua_Number n) { lua_pushnumber  (mystate, n); }
  Script pushstring   (const char * s)
  { lua_pushstring  (mystate, s);       }
  Script pushnil      (Script)
  { lua_pushnil     (mystate);          }
  Script pushcfunction     (lua_CFunction f)
  { lua_pushcfunction    (mystate, f);  }
  Script pushlightuserdata (Script * p)
  { lua_pushlightuserdata (mystate, p); }


  int  getgccount       (Script) { return lua_getgccount     (mystate); }
  int  getgcthreshold   (Script) { return lua_getgcthreshold (mystate); }
  Script setgcthreshold  (int newthreshold)
  { lua_setgcthreshold ( mystate, newthreshold); }
*/
  /* Low level garbage collector control. */
/* Creates new full user data block */
/*
  Script *newuserdata (size_t size) 
  { return lua_newuserdata (mystate, size); }

  int getmetatable(int objindex = STACKTOP)
  { return lua_getmetatable (mystate, objindex); }
  int setmetatable(int objindex = STACKTOP)
  { return lua_setmetatable (mystate, objindex); }
*/
  /* Low level metatable operations. */
/*
  Script newtable (Script)      { lua_newtable (mystate);         }
  Script gettable (int index) { lua_gettable (mystate,  index); }
  Script rawget   (int index) { lua_rawget   (mystate,  index); }
  Script settable (int index) { lua_settable (mystate,  index); }
  Script rawset   (int index) { lua_rawset   (mystate,  index); }
  int next      (int index) { return lua_next (mystate,  index); }

  Script rawgeti  (int index, int n) { lua_rawgeti(mystate, index, n); }
  Script rawseti  (int index, int n) { lua_rawseti(mystate, index, n); }
*/

/*
  Script call(int nargs, int nresults = LUA_MULTRET)
  { lua_call (mystate, nargs, nresults); }
*/
  /* Low level function call. May be unsafe.  */
/*
  int pcall(int nargs, int nresults = LUA_MULTRET, int errfunc = 0)
  { return lua_pcall (mystate, nargs, nresults, errfunc) ; }
*/
  /* Protected function call. Use this one in stead of call() for safe caling.  */
/*
  int docall(int nargs =0, int nresults =0)
  { int aid = pcall(nargs, nresults);
    if(aid)  { seterror(tostring()) ;}
    return aid; 
  }
*/
  /*
     Simplified protected call. Assumes that all variables on the stack,
     apart from one (the function) are arguments for that function.
     By default, assumes no return values.
  */

//  Script error(const char *message=0) { lua_error(mystate); }
  /* Low level error-raising function. */
//  Script concat(int n) { lua_concat (mystate, n); }
  /* Concatenates strings on the lua stack. */
/*
  Script fregister(const char *name, lua_CFunction func)
  { lua_register(mystate, name, func) ; }
*/
  /*
     Naming scheme exception. register is a keyword.
     You can register simple function names.
     More complex names, which include table indirection do NOT work.
  */
/*
  Script pushclosure (lua_CFunction fn, int n)
  { lua_pushcclosure (mystate, fn, n); }
*/
  /* Pushes a C closure. */
/*
  lua_State *GetState(Script)  { return mystate; }
*/
/*
  bool pushpointer(Script * ptr)
  {  
     Script **aid;      
     aid = (Script **) newuserdata(sizeof(Script*)); 
     if(!aid) return false   ;  
     (*aid) = ptr;
     return true;     
  }
  
  Script *topointer(int index = STACKTOP)
  {  
     Script **aid;     
     aid = (Script **) touserdata(index);
     if(!aid) return 0;     
     return (*aid);
  }
*/
#endif  
#ifdef _NO_WAY  

  /* tableintable  gets a table in a table with name name on stack index index,
         and creates a new table if it doesln't already exist. Returns 0 if on table 
        created, 1 if a table was created, and negative on error.         
  */
  int tableintable(const char *name, int index = LUA_REGISTRYINDEX)
  {
    pushstring(name); /* "name" */
    rawget(index);    /* name? */
    if(isnil(-1)) 
    {
        remove(-1);
        newtable();          /* {} */
        pushstring(name);  /* {}, name */
        pushvalue(-2);   /*{}, name, {}*/    
        settable(index);
        return 1;    /* Leave the new table on the stack top. */
    }
    return 0; /* fetched table is on the stack top.. */   
  }

   bool pushpointermetatable
   /* Pushes a full userdata pointer, and sets it's metatable
          to be the named one from either the registry or global namespace. */ 
   (Script * ptr, const char * metatablename, int metaindex = LUA_REGISTRYINDEX)
   { 
      if(!pushpointer(ptr)) return false;
      tableintable(metatablename, metaindex);
      setmetatable(-2);   
      return true;     
   }

#endif

#define tsukilua_boxpointer(L,u) \
        (*(Script **)(l.newuserdata(L, sizeof(Script *))) = (u))

#define tsukilua_unboxpointer(L,i)   (*(Script **)(lua_touserdata(L, i)))


/*
   Some macros to facilitate C++ integration.
 */


#define TSUKILUA_DECLARE(FUNCTIONNAME, CLASSNAME) \
  static int l_##FUNCTIONNAME(lua_State *) \
  { return FUNCTIONNAME(); }

#define TSUKILUA_INSTALL(FUNCTIONNAME) \
  do { fregister( #FUNCTIONNAME , l_##FUNCTIONNAME) ;} while(0)


